﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Lync.Model;
using Microsoft.Lync.Model.Group;
using System.DirectoryServices;
using System.Collections;
using System.Configuration;

namespace LDCM15
{
    struct User
    {
        public string UserSip;                       //sip адрес
        public string UserName;                      //имя
    }

    class Program
    {
        #region Fields

        static LyncClient lyncClient;
        static DirectoryEntry Base;
        static string OU;                                   //Формат OU: "OU=Отдел продаж,OU=ЧП Рога и копыта,DC=microsoft,DC=com"
        static ArrayList OUs = new ArrayList();             //спискок дополнительных OU для добавления групп 


        static ArrayList UserList = new ArrayList();        //список пользователей
        static User user;                                   //переменная для временного хранения структуры с пользователем        
        static Group NewGroup;                              //группа
        static string CurrentUserName;                      //имя пользователя в клиенте Lync

        //берем из App.config  
        static string Domain = ConfigurationManager.AppSettings["Domain"];           //домен
        static string Login = ConfigurationManager.AppSettings["Login"];             //логин любого доменного аккаунта
        static string Password = ConfigurationManager.AppSettings["Password"];       //пароль от аккаунта
        static string GroupName = ConfigurationManager.AppSettings["GroupName"];     //имя группы, которая будет добавлена: берем из App.config, если необходимо сделать группу с именем отдела - оставить значение пустым
        static string Depth = ConfigurationManager.AppSettings["Depth"];             //глубина поиска в AD 1-глубоко, 0 текущий OU


        #endregion

        static void Main(string[] args)
        {
            try
            {
                Console.OutputEncoding = Encoding.GetEncoding(866);
                AddDefaultGroup();          //добавить группу по-умолчанию
                AddAdditionalGroups();      //добавить дополнительные группы

                echo("\r\nОперация завершена, нажмите любую клавишу для продолжения");
                Console.ReadKey();
            }
            catch (Exception z)
            {
                echo("\r\nДействие завершилось с ошибкой, нажмите любую клавишу для закрытия окна. Ошибка:", z.Message);
                Console.ReadKey();
            }
        }

        /////////////////////////////////////////////////////////////////////////////////////////////////
        ////
        ////
        //// Lync
        ////
        ////
        /////////////////////////////////////////////////////////////////////////////////////////////////
        #region Lync

        /// <summary>
        /// Возвращает хэндлер клиента
        /// </summary>
        /// <returns></returns>
        static void GetClient()
        {
            try
            {
                lyncClient = LyncClient.GetClient();
            }
            catch (ClientNotFoundException)
            {
                Exit("Клиент не запущен. Запустите клиент и попробуйте снова.");
            }
        }

        /// <summary>
        /// Получает имя пользователя, который авторизован в клиенте Lync
        /// </summary>
        /// <param name="lyncClient"></param>
        /// <returns></returns>
        static void GetUserName()
        {
            try
            {
                CurrentUserName = lyncClient.Self.Contact.GetContactInformation(ContactInformationType.DisplayName) as string;
                echo("Производится операция над пользователем: ", CurrentUserName);
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }

        }

        /// <summary>
        /// Добавляет пользователю группу GroupName, затем запускает добавление пользователей сюда же
        /// </summary>
        /// <param name="GroupName">Имя добавляемой группы</param>
        static void AddACustomGroup()
        {
            try
            {
                //если группа без имени (записана как пустая) взять имя из AD
                if (GroupName == "")
                {
                    GroupName = OU.Remove(OU.IndexOf(',')).Remove(0, 3);
                }

                echo("Добавляем в контакт лист группу: ", GroupName);

                if (!lyncClient.ContactManager.Groups.TryGetGroup(GroupName, out NewGroup))
                {
                    lyncClient.ContactManager.EndAddGroup(lyncClient.ContactManager.BeginAddGroup(GroupName, null, lyncClient.ContactManager));
                    echo("Группа добавлена");
                }
                else
                {
                    Console.WriteLine("Такая группа уже существует, операция пропущена");
                }

                AddUsersToGroups();
            }
            catch (Exception ex)
            {
                echo("Произошла ошиба во время добавления группы. ",ex.Message);
            }
        }

        /// <summary>
        /// Добавляет пользователей из массива в группу GroupName
        /// </summary>
        static void AddUsersToGroups()
        {
            try
            {
                echo("Производится добавление пользователей в группу:");
                if (lyncClient.ContactManager.Groups.TryGetGroup(GroupName, out NewGroup))
                {
                    foreach (User obj in UserList)
                    {
                        if (NewGroup.Contains(lyncClient.ContactManager.GetContactByUri(obj.UserSip)) == false)
                        {
                            NewGroup.EndAddContact(NewGroup.BeginAddContact(lyncClient.ContactManager.GetContactByUri(obj.UserSip), null, null));
                            Console.WriteLine("{0} - добавлено", obj.UserName);
                        }
                        else
                        {
                            Console.WriteLine("{0} - пользователь уже существует", obj.UserName);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                echo("Произошла ошиба во время добавления пользователей. ",ex.Message);
            }
        }

        #endregion


        /////////////////////////////////////////////////////////////////////////////////////////////////
        ////
        ////
        //// AD
        ////
        ////
        /////////////////////////////////////////////////////////////////////////////////////////////////
        #region AD

        /// <summary>
        /// "Соединение" с AD
        /// </summary>
        /// <returns></returns>
        static void GetDirectoryEntry()
        {
            Base = new DirectoryEntry("LDAP://" + Domain, Login, Password);
            echo("\r\nCоединение с доменом: ", Domain);
        }

        /// <summary>
        /// "Соединение" с AD(по пути с OU)
        /// </summary>
        /// <returns></returns>
        static DirectoryEntry GetDirectoryEntry(string OU)
        {
            DirectoryEntry Base = new DirectoryEntry("LDAP://" + Domain + "/" + OU, Login, Password);
            return Base;
        }

        /// <summary>
        /// Возвращает список ВСЕХ пользователей из OU, у которых есть sip адрес, затем запускает на выполнение добавление группы, если завершилось удачно
        /// </summary>
        /// <param name="OU"></param>
        /// <returns></returns>
        static void StartAddGroup(string OU)
        {
            try
            {
                UserList.Clear();
                echo("Запрос пользователей из OU");
                DirectoryEntry entry = GetDirectoryEntry(OU);
                DirectorySearcher searcher = new DirectorySearcher(entry);
                searcher.SearchScope = (Depth == "0") ? SearchScope.OneLevel : SearchScope.Subtree;//глубина поиска
                searcher.ReferralChasing = ReferralChasingOption.All;
                searcher.Filter = "(&(objectClass=user)(msRTCSIP-PrimaryUserAddress=*))";

                System.DirectoryServices.SearchResultCollection results = searcher.FindAll();
                foreach (System.DirectoryServices.SearchResult result in results)
                {
                    DirectoryEntry directoryEntry = result.GetDirectoryEntry();
                    if ((directoryEntry.Properties["name"][0].ToString()) != CurrentUserName)
                    {
                        user.UserSip = directoryEntry.Properties["msRTCSIP-PrimaryUserAddress"][0].ToString();
                        user.UserName = directoryEntry.Properties["Name"][0].ToString();
                        UserList.Add(user);
                    }
                }

                AddACustomGroup();    
            }
            catch (Exception ex)
            {
                echo("Произошла ошиба во время во время запроса. ", ex.Message);
            }
        }

        /// <summary>
        /// Функция, которая возвращает путь к OU пользователя в AD.
        /// </summary>
        /// <param name="userName"></param>
        /// <returns></returns>
        private static String FindName(String userName)
        {
            string sName = "CN=" + userName + ",";//?                        
            try
            {
                DirectorySearcher search = new DirectorySearcher(Base);
                search.Filter = "(displayName=" + userName + ")";
                search.PropertiesToLoad.Add("distinguishedName");

                System.DirectoryServices.SearchResult result = search.FindOne();

                if (result != null)
                {
                    OU = result.Properties["distinguishedName"][0].ToString().Replace(sName, "");
                    echo("Извлечено месторасположение пользователя: ", OU);
                }
                else
                {
                    OU = "";
                    echo("Не удалось обнаружить пользователя: ", OU);
                }
            }
            catch (Exception ex)
            {
                Exit("Ошибка при соединении с AD. " + ex.Message + "Проверьте правильность параметров конфигурационного файла");
            }
            return "";
        }

        #endregion


        /////////////////////////////////////////////////////////////////////////////////////////////////
        ////
        ////
        //// Additional
        ////
        ////
        /////////////////////////////////////////////////////////////////////////////////////////////////
        #region Additional

        /// <summary>
        /// Везде подключиться, инициализировать часть необходимых компонентов, а затем добавить в Lync группу с текущим OU пользователя
        /// </summary>
        static void AddDefaultGroup()
        {
            GetClient();
            GetUserName();
            GetDirectoryEntry();
            FindName(CurrentUserName);
            //OU = "OU=lync,OU=Test,OU=Сектор ППП,OU=КБ-3 НИО,OU=НИО,OU=ГП ЗМКБ Прогресс,DC=nio,DC=com"; //временное
            StartAddGroup(OU);              
        }

        /// <summary>
        /// При наличии дополнительных OU в app.config - добавитьответствующие группы
        /// </summary>
        static void AddAdditionalGroups()
        {
            GetOUs();
            if (OUs.Count > 0)
            {
                foreach (object ou in OUs)
                {
                    GroupName = "";
                    OU = ou.ToString();
                    if (OU != "")
                    {
                        echo("\r\nДобавление пользователей из: ", OU);
                        StartAddGroup(OU);
                    }
                }            
            }
        }

        /// <summary>
        /// Берем из app.config список дополнительных OU
        /// </summary>
        static void GetOUs()
        {
            try
            {
                //если поле не пустое - разбить на строки через разделитель ;
                if (ConfigurationManager.AppSettings["OUs"] != "")
                {
                    string[] TEMP = ConfigurationManager.AppSettings["OUs"].Split(';');
                    foreach (string ous in TEMP)
                    {
                        OUs.Add(ous.Trim());
                    }
                    OUs = uniqMas(OUs);
                }
             }
            catch (Exception)
            {
                //ну не получилось извлечь, значит их нет
            }
        }
        
        /// <summary>
        /// Удалить возможные повторения OU в OUs
        /// </summary>
        /// <param name="mas"></param>
        /// <returns></returns>
        static ArrayList uniqMas(ArrayList mas)
        {
            int len = mas.Count;
            mas.Sort();
            for (int i = 0; ; i++)
            {               
                if (i != len - 2)
                {
                loop:
                    int num = mas.IndexOf(mas[i], i + 1);
                    if (num != -1)
                    {
                        mas.RemoveAt(num);
                        len = len - 1;
                        goto loop;
                    }
                }
                if (i == len - 1)
                    break;
            }
            return mas;
        }

        /// <summary>
        /// Выводит строку в консоль и делает паузу в пол секунды
        /// </summary>
        /// <param name="str">Сообщение</param>
        static void echo(string str)
        {
            Console.WriteLine(str);
            System.Threading.Thread.Sleep(500);
        }

        /// <summary>
        /// Выводит строку в консоль и делает паузу в пол секунды (2 параметра)
        /// </summary>
        /// <param name="str"></param>
        /// <param name="par"></param>
        static void echo(string str, string par)
        {
            Console.WriteLine("{0}{1}", str, par);
            System.Threading.Thread.Sleep(500);//так красивше выводится
            
        }

        /// <summary>
        /// Завершение консольного приложения, в случае обнаружения ошибки, после которой работа программы далее бессмысленна
        /// </summary>
        /// <param name="message"></param>
        static void Exit(string message)
        {
            Console.WriteLine(message);
            Console.WriteLine("Нажмите любую клавишу для закрытия приложения");
            Console.ReadKey();
            Environment.Exit(0);
        }

        #endregion
    }
}
